package com.jt;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Random;

/**
 * @author 金帮嘉诚 @date 2021-08-17 星期二 10:49
 */
//@EnableFeignClients注解描述启动类时,用于告诉springboot在启动时,
//扫描启动类所在包或子包中的类,假如接口上有@FeignClient注解描述,则对
//这样的接口创建其实现类,在实现类内部帮我们进行远程服务调用
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    //*********************************************************************************************








    //*****************************四种请求方式*******************************************************

    /**创建RestTemplate对象,然后基于此对象进行远程服务调用
     * @Bean 注解用于描述方法,用于告诉spring框架这个方法的返回值交给spring管理,
     * spring默认会这个方法返回对象起一个bean的名字(key),默认为方法名.当然也可以
     * 对一个bean的名字进行自定义,例如@Bean("自己定义的名字").
     * 思考:spring框架中为什么会给出这样的注解,用于描述方法,在方法中构建对象?
     * 第一:Spring是一个资源整合框架.
     * 第二:何为资源?(内存中对象)
     * 第三:所有对象是否都有类型?
     * 第四:所有对象的类型都是你自己定义的吗?不一定(有自己,有第三方)
     * 第五:第三方的类,我们能在类上直接添加注解描述吗(例如@Component)?不可以
     */
    @Bean//如果第三方写的类 要交给Spring管理 需要加该注解 才能在之后使用时加@Autowire自动管理
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    @Bean
    @LoadBalanced
    public RestTemplate loadBalancerRestTemplate(){
        return new RestTemplate();
    }

    @RestController
    @RequestMapping("/consumer")
    public class ConsumerController{
        @Value("${server.port:8080}")
        private String port;
        /**
         * 使用@Autowired 注解 描述属性时 会告诉Spring框架 要优先按属性类型进行对象的查找和注入,
         * 假如此类型的对象存在多个,此时还会按照属性名进行查找比对,
         * 有相同的则直接注入(DI),没有相同的则出错
         * 当然也可以再添加一个注解@Qualifier,指定要注入的具体对象
         */
        @Autowired
        private RestTemplate restTemplate;

        @Autowired//从nacos获取服务对象
        private LoadBalancerClient loadBalancerClient;//idea ctrl h eclipse ctrl t 查看继承结构

        //从nacos获取服务列表--用算法选择服务实例
        @Autowired
//        @Qualifier("loadBalancerRestTemplate")
        private RestTemplate loadBalancerRestTemplate;

        /**
         * 向提供者发送请求的演示 使用RestTemplate对象发送请求(底层使用HttpClient对象)
         * 用Spring管理(@Bean,用于让Spring管理第三方类)
         * url:http://localhost:8090/consumer/echo1/{msg}
         * */
        @GetMapping("/echo1/{msg}")
        public String echo1(@PathVariable String msg){
            String url = "http://localhost:8080/provider/echo/"+msg;
            return restTemplate.getForObject(url,String.class);
        }

        /**
         * 手动负载均衡 只是演示 不灵活
         * url:http://localhost:8090/consumer/echo2/{msg}
         * */
        @GetMapping("/echo2/{msg}")
        public String echo2(@PathVariable String msg){
            String url0 = "http://localhost:8080/provider/echo/"+msg;
            String url1 = "http://localhost:8081/provider/echo/"+msg;
            String[] urls = new String[]{url0,url1};
            int anInt = new Random().nextInt(urls.length);
            return restTemplate.getForObject(urls[anInt],String.class);
        }

        /**
         * 使用对象实现负载均衡 LoadBalancerClient使用了ribbon
         * url:http://localhost:8090/consumer/echo3/{msg}
         * */
        @GetMapping("/echo3/{msg}")
        public String echo3(@PathVariable String msg){
            String serviceId = "sca-provider";//名字要在nacos服务列表中
            ServiceInstance choose = loadBalancerClient.choose(serviceId);
            String ip = choose.getHost();
            int server_port = choose.getPort();
//            String url="http://"+ip+":"+server_port+"/provider/echo/"+msg;
            String url = String.format("http://%s:%s/provider/echo/%s",ip,server_port,msg);
            return restTemplate.getForObject(url,String.class);
        }

        /**
         * 代码简化的负载均衡 在调用RestTemplate对象时增加了拦截器 运行会慢一点
         * url:http://localhost:8090/consumer/echo4/{msg}
         * */
        @GetMapping("/echo4/{msg}")
        public String echo4(@PathVariable String msg){
            String serviceId = "sca-provider";
            String url = String.format("http://%s/provider/echo/%s",serviceId,msg);
            return loadBalancerRestTemplate.getForObject(url,String.class);
        }
    }
}
